/*
--------------------------------------------------------------------------------
				Ak-Lib. 
			 Project       	: Ak-Lib
			 Group         	: Ak-Lib R&D Team
			 Author        	: Ocean.Wang
			 Create Date 	: 05-01-2010
--------------------------------------------------------------------------------
Code Name 	:  A.K 	~v~
Description	:  Yet Another Cross-Platform Lib.
Cautions    	:
--------------------------------------------------------------------------------
Change History:
Version		: 0.1
Building 		: 05/01/10
10-05.01 first draft, build, debug and test.
--------------------------------------------------------------------------------
*/
#include "ak_types.h"
#include "ak_vfs.h"

static 	int 	_gTraceLvl_Vfs = TRACELVL_NORMAL;


#define SDIR_MK_MODE 	(S_IRWXU|S_IRWXG|S_IROTH|S_IXOTH)
#define SDIR_LS_MAX 	256

/* VFS Mode */
#define SVFS_MODE_FILE	0
#define SVFS_MODE_DIR 	1

#define SVFS_MAX_FITEM 		10000
#define SVFS_MAX_FCOUNT 	10

/* GC: Dir Left, File Left */
#define SVFS_MAX_DLEFT 		50
#define SVFS_MAX_FLEFT 		100

static 	int 	_SDir_SortASCII(const void * pXA, const void * pXB)
{
	_t_vfs_clue * pClueA;
	_t_vfs_clue * pClueB;
	
	pClueA = (_t_vfs_clue *)pXA;
	pClueB = (_t_vfs_clue *)pXB;

	if((pClueA->type == SVFS_MODE_DIR) && (pClueB->type != SVFS_MODE_DIR)){
		return -1;
	}
	if((pClueA->type != SVFS_MODE_DIR) && (pClueB->type == SVFS_MODE_DIR)){
		return 1;
	}	

	/* Judge the String */
	return SYS_STRCMP(pClueA->sPath, pClueB->sPath);
}


/* List & Sort, Using the Flag :
** 0: All, Include (Dir, File)
** 1: File
** 2: Dir
*/

#define SVFS_CLUE_SET(pClue, vPath, vStat, vCount, vLen) \
	do{\
		/* dup the path */ 	\
		SYS_MEMCPY((pClue)->sPath, vPath, vLen);	\
		(pClue)->sPath[vLen] = 0;					\
		(pClue)->size = (vStat).st_size;			\
		/* next */			\
		(vCount)  	+= 1;	\
		(pClue) 	+= 1;	\
	}while(0)

	
int 	SDir_Ls(const char * path, int flag, _t_vfs_clue * pClueAU, int  limit)
{
	int 				len, count;
	DIR 			* 	dir;
	struct dirent 	* 	pDirent;
	_t_vfs_clue 	* 	pClue;
	struct stat64 		dStat;
	char 				sPath[S_APP_MAX_PATH];

	count = 0;
	pClue = pClueAU;
	if(path && pClue && (limit > 0)){
		/* Dir Open */
		dir = opendir(path);
		if(dir){
			count = 0;
			while((pDirent = readdir(dir))){
				/* the full path */
				len = SYS_SNPRINTF(sPath, (S_APP_MAX_PATH - 1), "%s/%s", path, pDirent->d_name);	
				
				if((len > 0) && (stat64(sPath, &dStat) == 0)){
					sPath[len] = 0;
					/* check if dir */
					switch(flag){
						case 0:
							if(S_ISDIR(dStat.st_mode)){
								pClue->type = SVFS_MODE_DIR;
							}else{
								pClue->type = SVFS_MODE_FILE;
							}
							/* Clue Set */
							SVFS_CLUE_SET(pClue, pDirent->d_name, dStat, count, len);
							break;

						case 1:
							if(!(S_ISDIR(dStat.st_mode))){
								pClue->type = SVFS_MODE_FILE;
								/* Clue Set */	
								SVFS_CLUE_SET(pClue, pDirent->d_name, dStat, count, len);
							}
							break;

						case 2:
							if(S_ISDIR(dStat.st_mode)){
								pClue->type = SVFS_MODE_DIR;
								/* Clue Set */	
								SVFS_CLUE_SET(pClue, pDirent->d_name, dStat, count, len);
							}
							break;

						default:
							break;
					}
					if(count >= limit){
						break;
					}
				}
			}
		}
		/* Dir Close */
		closedir(dir);
	}

	if(count > 0){
		/* Let's Sort & Reorder */
		qsort(pClueAU, count, sizeof(_t_vfs_clue),  _SDir_SortASCII);	
	}
	return count;
}


int 	SDir_Mk(const char * name)
{
	int 	ret;
	char 	sPath[S_APP_MAX_PATH];

	ret = -1;
	if(name){
		/* Try the Path of ./ */
		SYS_SNPRINTF(sPath, (S_APP_MAX_PATH - 1), "%s", name);
		ret = mkdir(sPath, SDIR_MK_MODE);
		#if 0
		if(ret != 0){
			/* Try the Path of /tmp */
			SYS_SNPRINTF(sPath, (S_APP_MAX_PATH - 1), "/tmp/%s", name);	
			ret = mkdir(sPath, SDIR_MK_MODE);	
		}
		#endif
	}

	return ret;
}


int 	SDir_CmdTar(const char * path, const char * name)
{
	int 				len;
	char 				sCmd[S_APP_MAX_PATH * 2];
	if(path){
		len = SYS_SNPRINTF(sCmd, (S_APP_MAX_PATH * 2 - 1), "tar jcf %s/%s.tar.bz2 %s/%s", 
				path, name, 
				path, name);	
		if(len > 0){
			system(sCmd);
			return 0;
		}
	}
	return -1;
}


int 	SDir_CmdMv(const char * src, const char * dst)
{
	int 				len;
	char 				sCmd[S_APP_MAX_PATH * 2];
	if(src && dst){
		len = SYS_SNPRINTF(sCmd, (S_APP_MAX_PATH * 2 - 1), "mv %s %s", src, dst);	
		if(len > 0){
			system(sCmd);
			return 0;
		}
	}
	return -1;
}


int 	SDir_CmdRm(const char * path)
{
	int 				len;
	char 				sCmd[S_APP_MAX_PATH * 2];
	if(path){
		len = SYS_SNPRINTF(sCmd, (S_APP_MAX_PATH * 2 - 1), "rm -rf %s", path);	
		if(len > 0){
			system(sCmd);
			return 0;
		}
	}
	return -1;
}


/* Add Support for Indexing of Files */
static	int 	_SVfs_ChkDir(_t_vfs_hi* pHI, const char * path, int flag)
{
	int 				ret;
	_t_vfs_ti 			last;
	struct stat64 		dStat;
	char 				dName[S_APP_MAX_PATH];

	/* Create New Dir */
	SVfs_Timing(&last);
	SYS_SNPRINTF(dName, (S_APP_MAX_PATH-1), "%03d_%02d_%02d_%02d_%02d_%02d",
			last.t_year,
			last.t_mon,
			last.t_mday,
			last.t_hour,
			last.t_min,
			last.t_sec);
	
	if((path) && (SYS_STRLEN(path) < (S_APP_MAX_PATH - 10))){
		if(flag == 1){
			SYS_SNPRINTF(pHI->wPath, (S_APP_MAX_PATH-1), "%s", path);
		}
		SYS_SNPRINTF(pHI->dPath, (S_APP_MAX_PATH-1), "%s/%s", path, dName);
	}else{
		if(flag == 1){
			SYS_SNPRINTF(pHI->wPath, (S_APP_MAX_PATH-1), "/tmp/");
		}
		SYS_SNPRINTF(pHI->dPath, (S_APP_MAX_PATH-1), "/tmp/%s", dName);
	}
	/*  Make Sure the Working Dir is Open */
	ret = stat64(pHI->wPath, &dStat);
	if(ret != 0){
		SDir_Mk(pHI->wPath);
	}
	ret = stat64(pHI->dPath, &dStat);
	if(ret != 0){
		SDir_Mk(pHI->dPath);
	}
	

	return 0;
}

static	int 	_SVfs_ChkFile(_t_vfs_hi* pHI)
{
	int 				ret, offset;
	_t_vfs_ti 			last;
	struct stat64 		dStat;
	char				fPath[S_APP_MAX_PATH];

	ret = -1;
	if(pHI){
		/* Close the Index & Data Handle */
		if(pHI->hIndex){
			fclose(pHI->hIndex);
			pHI->hIndex = NULL;
		}
		if(pHI->hData){
			fclose(pHI->hData);
			pHI->hData = NULL;
		}

		#if 0
		_SVfs_ChkDir(pHI, path, 0);
		#endif
		
		/* First Check if Has the Index File :
		** a+
		** Open for reading and appending (writing at end of file).
		** The file is created if it does not exist. 
		** The initial file position for reading is at the beginning of the file, 
		** but output is always appended to the end of the file.
		*/
		SYS_SNPRINTF(fPath, (S_APP_MAX_PATH-1), "%s/index", pHI->dPath);
		/* Set the iCount */
		pHI->iTotal = 0;
		ret = stat64(fPath, &dStat);
		if(ret == 0){
			pHI->iTotal= dStat.st_size / sizeof(_t_vfs_ti);
		}
		
		/* Checking the Total, May New Dir */
		if(pHI->iTotal >= (SVFS_MAX_FCOUNT * SVFS_MAX_FITEM)){
			_SVfs_ChkDir(pHI, pHI->wPath, 0);
			/* Trigger the GC */
		}

		/* Calc the iData, iOffset */
		offset 		= 0;
		last.i_off 	= 0;
		last.i_len 	= 0;
		
		pHI->iOffset= 0;
		pHI->hIndex	= fopen(fPath, "a+");
		if(pHI->iTotal > 0){
			/* Seek the Offset */
			fseek(pHI->hIndex, (pHI->iTotal -1)*sizeof(_t_vfs_ti), SEEK_SET);
			fread(&last, sizeof(_t_vfs_ti), 1, pHI->hIndex);
			offset = last.i_off + last.i_len;
		}
		/* Set the iData */
		pHI->iData 		= (pHI->iTotal / SVFS_MAX_FITEM);
		
		SYS_SNPRINTF(fPath, (S_APP_MAX_PATH-1), "%s/data.%04d", pHI->dPath, pHI->iData);
		if((pHI->iTotal % SVFS_MAX_FITEM) != 0){
			/* Trunc the Data File */
			truncate(fPath, offset);
			
			/* Set the iOffset */
			pHI->iOffset = ret;
		}
		pHI->hData	= fopen(fPath, "a+");

		/* Index & Data File */
		SYS_TRACE(_gTraceLvl_Vfs, TRACELVL_DEBUG, "Seek Idx:%d Off:%d Last:(%d - %d)\n", 
					pHI->iTotal,	pHI->iData, 	offset,
					last.i_off, 	last.i_len);

		ret = 0;
	}
	
	return ret;
}



int 	SVfs_RecFile(_t_vfs_hi * pHI, _t_vfs_ti * pTi, void * pData, int lData)
{
	int 				ret;

	ret = -1;
	if(pHI && pTi && pData && (lData > 0)){
		if(	!(pHI->hIndex) || 
			!(pHI->hData)  ||
			((pHI->iTotal / SVFS_MAX_FITEM) != pHI->iData)){
			_SVfs_ChkFile(pHI);
		}

		/* Setting the Ti */
		pTi->i_idx = (pHI->iTotal % SVFS_MAX_FITEM);
		pTi->i_off = (pHI->iOffset);
		pTi->i_len = lData;
		
		if(pHI->hIndex && pHI->hData){
			/* Write Data & Index File */
			fwrite(pData,lData, 1, pHI->hData);
			fwrite(pTi, sizeof(_t_vfs_ti), 1, pHI->hIndex);

			/* Update the HI */
			pHI->iTotal 	+= 1;
			pHI->iOffset   	+= lData;
			#if 1
			fflush(pHI->hData);
			fflush(pHI->hIndex);
			#endif

			ret = 0;
		}
		
	}
	
	return ret;
}


/* ---------------------------------------------------------------------------
** Add the support of GC:
** 1. Trigger.
** 2. Schedule.
*/

int 	SVfs_GcInit(_t_vfs_gc * pGc)
{
	int 	ret;

	ret = -1;
	if(pGc){
		SYS_MEMZERO(pGc, sizeof(_t_vfs_gc));
		SYS_INIT_CS(&(pGc->lock));
		SYS_SEM_B_INIT(&(pGc->wake));
		pGc->rdIx 	= 
		pGc->wrIx 	= 0;
		pGc->exFlag = 1;

		ret = 0;
	}
	return ret;
}

int 	SVfs_GcTrigger(_t_vfs_gc * pGc, char * pPath, int lPath)
{
	int 	ret;
	char * 	wPath;

	ret = -1;
	if(pGc && pPath && (lPath > 0) && (lPath < S_APP_MAX_PATH)){
		SYS_ENTER_CS(&(pGc->lock)); 
		if(((pGc->wrIx + 1)% SVFS_MAX_GC) != pGc->rdIx){
			wPath = pGc->wPathA[pGc->wrIx];
			SYS_MEMCPY(wPath, pPath, lPath);
			wPath[lPath] = 0;
			pGc->wrIx = (pGc->wrIx + 1)% SVFS_MAX_GC;
			ret = 0;
		}
		SYS_LEAVE_CS(&(pGc->lock)); 
		SYS_SEM_B_GIVE(&(pGc->wake));
	}

	return ret;
}


int 	SVfs_GcSche(_t_vfs_gc * pGc)
{
	int 			i, ret, count;
	struct stat64 	dStat;
	char * 			wPath;
	char 			path[S_APP_MAX_PATH];
	char 			sPath[S_APP_MAX_PATH];
	_t_vfs_clue 	clue[SVFS_MAX_FLEFT];
	
	if(pGc){
		do{
			SYS_SEM_B_TAKE(&(pGc->wake), _WAIT_FOREVER);
			while(pGc->rdIx != pGc->wrIx){
				/* Retrive the wPath */
				SYS_ENTER_CS(&(pGc->lock)); 
				if(((pGc->wrIx + 1)% SVFS_MAX_GC) != pGc->rdIx){
					wPath = pGc->wPathA[pGc->rdIx];
					SYS_MEMCPY(path, wPath, S_APP_MAX_PATH);
					pGc->rdIx = (pGc->rdIx + 1)% SVFS_MAX_GC;
				}
				SYS_LEAVE_CS(&(pGc->lock)); 


				/* Checking the Working Path, Tar */
				ret = stat64(path, &dStat);
				if((ret == 0) && S_ISDIR(dStat.st_mode)){
					/* Step 1: Checking the Dir */
					count = SDir_Ls(path, 2, clue, (SVFS_MAX_DLEFT - 1));
					for(i=0; i<(count - 1); i++){
						SDir_CmdTar(path, clue[i].sPath);
						ret = SYS_SNPRINTF(sPath, (S_APP_MAX_PATH - 1), "%s/%s", path, clue[i].sPath);
						if(ret > 0){
							SDir_CmdRm(sPath);
						}
					}

					/* Step 2: Checking the Tar File */
					count = SDir_Ls(path, 1, clue, (SVFS_MAX_FLEFT - 1));
					if(count > SVFS_MAX_DLEFT){
						for(i=0; i<(count - SVFS_MAX_DLEFT); i++){
							ret = SYS_SNPRINTF(sPath, (S_APP_MAX_PATH - 1), "%s/%s", path, clue[i].sPath);
							if(ret > 0){
								SDir_CmdRm(sPath);
							}
						}
					}
				}
				
			}
			
			ret = 0;
		}while(pGc->exFlag == 1);
	}


	return 0;
}
